package com.illtamer.infinite.morii.service;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.illtamer.infinite.morii.mapper.MoriiVerifyMapper;
import com.illtamer.infinite.morii.pojo.FileTypeEnum;
import com.illtamer.infinite.morii.pojo.MoriiVerify;
import com.illtamer.infinite.morii.util.CommonUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

@Slf4j
@Service
public class MoriiVerifyService {

    @Resource(name = "moriiVerifyMapper")
    private MoriiVerifyMapper moriiVerifyMapper;

    /**
     * 增
     * @apiNote 当且仅当新增数据不在缓存 & 与数据库中 key 值不同时方可插入
     *      or {@link #update(MoriiVerify)}
     * */
    @CachePut(value = "basic", key = "#moriiVerify.groupId + '/' + #moriiVerify.artifactId + '/' + #moriiVerify.version")
    public MoriiVerify insert(MoriiVerify moriiVerify) {
        // 当 key 无缓存数据在库中时，此特判生效，并记录缓存
        MoriiVerify old = query(getMoriiVerifyKey(moriiVerify));
        if (old != null) {
            if (old.equals(moriiVerify)) return old;
            log.warn("Unexpected insert-update operation, id: {}", old.getId());
            update(moriiVerify);
            return old;
        }
        moriiVerifyMapper.insert(moriiVerify);
        return refreshVerifyMap(moriiVerify);
    }

    /**
     * 删
     * @param key groupId + / + artifactId + / + version
     * */
    @CachePut(value = "basic", key = "#key")
    public MoriiVerify delete(String key) {
        String[] splits = CommonUtil.splitKey(key, 3);
        QueryWrapper<MoriiVerify> wrapper = new QueryWrapper<>();
        wrapper.lambda()
                .eq(MoriiVerify::getGroupId, splits[0])
                .eq(MoriiVerify::getArtifactId, splits[1])
                .eq(MoriiVerify::getVersion, splits[2]);
        moriiVerifyMapper.delete(wrapper);
        return null;
    }

    /**
     * 改
     * @apiNote groupId artifactId version 相同，更新其余值
     * */
    @CachePut(value = "basic", key = "#moriiVerify.groupId + '/' + #moriiVerify.artifactId + '/' + #moriiVerify.version")
    public MoriiVerify update(MoriiVerify moriiVerify) {
        UpdateWrapper<MoriiVerify> wrapper = new UpdateWrapper<>();
        wrapper.lambda()
                .eq(MoriiVerify::getGroupId, moriiVerify.getGroupId())
                .eq(MoriiVerify::getArtifactId, moriiVerify.getId())
                .eq(MoriiVerify::getVersion, moriiVerify.getVersion())
                .eq(MoriiVerify::getCreateTime, moriiVerify.getCreateTime())
                .set(MoriiVerify::getVerifyMap, moriiVerify.getVerifyMap());
        moriiVerifyMapper.update(null, wrapper);
        return refreshVerifyMap(moriiVerify);
    }

    /**
     * 查
     * @param key groupId + / + artifactId + / + version
     * */
    @Cacheable(value = "basic", key = "#key")
    public MoriiVerify query(String key) {
        String[] splits = CommonUtil.splitKey(key, 3);
        QueryWrapper<MoriiVerify> wrapper = new QueryWrapper<>();
        wrapper.lambda()
                .eq(MoriiVerify::getGroupId, splits[0])
                .eq(MoriiVerify::getArtifactId, splits[1])
                .eq(MoriiVerify::getVersion, splits[2]);
        return refreshVerifyMap(moriiVerifyMapper.selectOne(wrapper));
    }

    public List<MoriiVerify> getPrefixMoriiVerifies(@NonNull String prefix) {
        return moriiVerifyMapper.getPrefixMoriiVerifies(prefix);
    }

    /**W
     * 获取 MoriiVerify 对应缓存格式的 key
     * */
    public String getMoriiVerifyKey(MoriiVerify moriiVerify) {
        return moriiVerify.getGroupId() +
                '/' +
                moriiVerify.getArtifactId() +
                '/' +
                moriiVerify.getVersion();
    }

    private static MoriiVerify refreshVerifyMap(MoriiVerify verify) {
        if (verify == null) return null;
        Map<FileTypeEnum, byte[][]> verifyMap = verify.getVerifyMap();
        if (verifyMap == null) verify.setVerifyMap(verifyMap = new TreeMap<>());
        verifyMap.put(FileTypeEnum.JAR, new byte[][] {
                verify.getJarASC().getBytes(StandardCharsets.UTF_8),
                verify.getJarMD5().getBytes(StandardCharsets.UTF_8),
                verify.getJarSHA1().getBytes(StandardCharsets.UTF_8)
        });
        verifyMap.put(FileTypeEnum.POM, new byte[][] {
                verify.getPomASC().getBytes(StandardCharsets.UTF_8),
                verify.getPomMD5().getBytes(StandardCharsets.UTF_8),
                verify.getPomSHA1().getBytes(StandardCharsets.UTF_8)
        });
        return verify;
    }

}
